package control.networking;

import java.awt.Color;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Iterator;

import javax.swing.ImageIcon;

import view.RiskFrame;
import view.SplashRiskFrame;
import view.SplashScreen;

import model.*;
import model.Map.GamePhase;
import model.Player.PlayerTurn;

/**
 * RiskClient serves as the backend for the GUI and interacts
 * with the RiskServerHandler.
 * 
 * @author Kevin Luikens
 * 
 */
public class RiskClient {
	private Player player;
	private int port;

	private Socket server;
	private static Object lock;

	private Map map;
	private Map mapBackup;
	private SplashScreen ss;
	private RiskFrame frame;

	private static ObjectOutputStream oos;
	private ObjectInputStream ois;

	/**
	 * Initializes the RiskClient, creating a connection to the server
	 * and spawning the necessary input and output streams.
	 * @param port The port to which the client will connect on the server.
	 */
	public RiskClient(int port) {
		Object input;
		lock = new Object();
		this.port = port;

		map = null;
		mapBackup = null;

		try {

			// Will have to change
			// the address
			// depending on
			// where we're
			// working

			server = new Socket("150.135.82.159", port);

			ois = new ObjectInputStream(server.getInputStream());
			oos = new ObjectOutputStream(server.getOutputStream());

			// TODO Gather this information from a pop-up dialog box
			player = new HumanPlayer("Cam", Color.RED, new ImageIcon(
					"images/players/300.png"));
			oos.writeObject(player);
			
			System.out.println("RC player ID " + player.getUUID());
			
			// ==============================================================

			oos.writeObject(new JoinGame());
			

			JoinGame join = (JoinGame) ois.readObject();
			if (join.getGameJoinable() == false
					&& join.getConfigInProcess() == false) {
				ss = new SplashScreen(true);
				oos.writeObject(ss.getGameConfig());
				
			} else {
				new SplashScreen(false);
			}

			synchronized (lock) {
				while (true) {
					input = ois.readObject();
					processObject(input);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Processes the generic object received by the
	 * ObjectInputStream read and determines what instance of a class it
	 * is, and takes an accordant action based upon such.
	 * @param obj The generic object with which readObject() returns. Determines
	 * what class it is an instance of.
	 * @throws InterruptedException
	 */
	private void processObject(Object obj) throws InterruptedException {

		if (obj instanceof Player) { // if UUID matches yours, it's your turn.
			// else, wait for new map.
			if (((Player) obj).getUUID().compareTo(player.getUUID()) == 0) {

				if (map.getPhase() == GamePhase.SELECTING
						|| map.getPhase() == GamePhase.PLACING) {
					frame.updatePlayerTurn(Player.PlayerTurn.START);
				} else {
					frame.updatePlayerTurn(Player.PlayerTurn.CARDS);
				}
			} else {
				frame.updatePlayerTurn(PlayerTurn.FALSE);
			} // do nothing? this should just send it back to the while loop.
		}

		if (obj instanceof Map) {
			if (map == null) {
				mapBackup = map;
				map = (Map) obj;
				// TODO Construct GUI here.
				Iterator<Player> it = map.getPlayerList().iterator();
				while (it.hasNext()) {
					Player temp = it.next();
					if (temp.getUUID().compareTo(player.getUUID()) == 0) {
						player = temp;
					}
				}
				frame = new SplashRiskFrame(map, this).getRiskFrame();
				new ChatClient(frame.getChatPanel()).start();
			} else {
				mapBackup = map;
				map = (Map) obj;
				frame.updateMap(map); // Notify the GUI to update.
			}
		}
	}

	/**
	 * Used to write the a new modified Map to the server when
	 * the AI client takes a new action in the game.
	 * @param map The new Map with which to update all the gamers with.
	 */
	public void updateWithNewMap(Map map) {
		mapBackup = map;
		this.map = map;
		try {
			oos.writeObject(map);
			oos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Spawns and writes a new TurnDone instance to notify the
	 * server that this client's turn is now done.
	 * @param p Player object
	 */
	public void endMyTurn(Player p) {
		try {
			oos.writeObject(new TurnDone(p));
			oos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Returns the player that this client represents
	 * @return Player object
	 */
	public Player getThisPlayer() {
		return player;
	}

	/**
	 * Main method that will start RiskClient
	 * @param args Optional command line arguments. 
	 */
	public static void main(String[] args) {
		new RiskClient(4001);
	}

}
